#include "qdevicemanager.h"

#include "qbasedefine.h"
#include "qdeviceserver.h"
#include "update/qserverupdate.h"
#include "data_sync/qserverdatasync.h"
#include "driver_listern/qserverdriverlistern.h"
#include "database/qsendfile.h"

#include "../shared/xmlobject.h"

#include <QDir>
#include <QApplication>

QDeviceManager::QDeviceManager(tagServerInfo *info, QObject *parent):
    QObject(parent),
    m_device_server(new QDeviceServer(info)),
    m_server_info(info),
    m_update_server(new QServerUpdate(m_device_server)),
    m_send_file(new QSendFile)
{
    connect(m_device_server,SIGNAL(device_offline(QString)),this,SLOT(delete_device(QString)));
    connect(m_device_server,SIGNAL(new_device(tagDeviceInfo*)),this,SLOT(new_device(tagDeviceInfo*)));
    connect(m_device_server,SIGNAL(property_refresh(QString,QStringList)),
            this,SLOT(property_refreach(QString,QStringList)));
    connect(m_device_server,SIGNAL(recv_data(QByteArray)),this,SLOT(recv(QByteArray)));
    m_device_server->search();
}

QDeviceManager::~QDeviceManager()
{
    delete m_send_file;
    qDeleteAll(m_devices);
    m_devices.clear();
    m_uuid_to_device.clear();
    delete m_device_server;

    qDeleteAll(m_uuid_to_data.values());
    m_uuid_to_data.clear();
    qDeleteAll(m_uuid_to_listern.values());
    m_uuid_to_listern.clear();
}

tagDeviceInfo* QDeviceManager::get_device(const QString &uuid)
{
    return m_uuid_to_device.value(uuid);
}

QList<tagDeviceInfo*>   QDeviceManager::get_devices()
{
    return m_devices;
}

void QDeviceManager::new_device(tagDeviceInfo *device)
{
    if(m_uuid_to_device.value(device->m_uuid)!=NULL)
    {
        return;
    }
    tagDeviceInfo *info=new tagDeviceInfo;
    *info=*device;
    m_devices.append(info);
    m_uuid_to_device.insert(info->m_uuid,info);
    emit device_online(info);
}

void QDeviceManager::delete_device(const QString &uuid)
{
    tagDeviceInfo *info=m_uuid_to_device.value(uuid);
    if(info!=NULL)
    {
        m_devices.removeAll(info);
        m_uuid_to_device.remove(uuid);
        emit device_offline(uuid);
        QServerDataSync* sync=m_uuid_to_data.value(uuid);
        if(sync!=NULL)
        {
            m_uuid_to_data.remove(uuid);
            delete sync;
        }
        QServerDriverListern *listern=m_uuid_to_listern.value(uuid);
        if(listern!=NULL)
        {
            m_uuid_to_listern.remove(uuid);
            delete listern;
        }
        delete info;
    }
}

QServerUpdate* QDeviceManager::get_update_server()
{
    return m_update_server;
}

void QDeviceManager::property_refreach(const QString &uuid, const QStringList &list)
{
    tagDeviceInfo *info=m_uuid_to_device.value(uuid);
    if(info!=NULL)
    {
        info->m_propertys=list;
        emit device_property_refreach(uuid,list);
    }
}

QServerDataSync* QDeviceManager::start_data_sync(const QString &uuid)
{
    tagDeviceInfo *info=m_uuid_to_device.value(uuid);
    if(info==NULL)
    {
        return NULL;
    }
    QServerDataSync* sync=new QServerDataSync;

    XMLObject xml;

    xml.set_title("Data_Sync");

    xml.set_property("Server_uuid",m_server_info->m_uuid);
    xml.set_property("Device_uuid",info->m_uuid);
    xml.set_property("Operator","Start");

    QString str=QString::number(DATA_SYCN_CODE)+":"+xml.write();
    m_device_server->send_data(str.toLocal8Bit(),info->m_device_ip);

    m_uuid_to_data.insert(uuid,sync);

    return sync;
}

QServerDriverListern* QDeviceManager::start_driver_listern(const QString &uuid)
{
    tagDeviceInfo *info=m_uuid_to_device.value(uuid);
    if(info==NULL)
    {
        return NULL;
    }
    QServerDriverListern* listern=new QServerDriverListern;

    XMLObject xml;

    xml.set_title("Driver_Listern");

    xml.set_property("Server_uuid",m_server_info->m_uuid);
    xml.set_property("Device_uuid",info->m_uuid);
    xml.set_property("Operator","Start");

    QString str=QString::number(DRIVER_LISTERN_CODE)+":"+xml.write();
    m_device_server->send_data(str.toLocal8Bit(),info->m_device_ip);

    m_uuid_to_listern.insert(uuid,listern);

    return listern;
}

void QDeviceManager::end_data_sync(const QString &uuid)
{
    QServerDataSync *sync=m_uuid_to_data.value(uuid);
    if(sync!=NULL)
    {
        m_uuid_to_data.remove(uuid);
        delete sync;
    }

    QServerDriverListern *listern=m_uuid_to_listern.value(uuid);
    if(listern!=NULL)
    {
        m_uuid_to_listern.remove(uuid);
        delete listern;
    }

    tagDeviceInfo *info=m_uuid_to_device.value(uuid);
    if(info!=NULL)
    {
        XMLObject xml;

        xml.set_title("Data_Sync");

        xml.set_property("Server_uuid",m_server_info->m_uuid);
        xml.set_property("Device_uuid",info->m_uuid);
        xml.set_property("Operator","End");

        QString str=QString::number(DATA_SYCN_CODE)+":"+xml.write();
        m_device_server->send_data(str.toLocal8Bit(),info->m_device_ip);
    }
}

void QDeviceManager::recv(const QByteArray &data)
{
    int index=data.indexOf(":");
    if(index<=0)
    {
        return;
    }
    QString code=data.left(index);
    int c=code.toInt();
    switch(c)
    {
    case DATA_SYCN_CODE:
        recv_sync_data(data.mid(index+1));
        break;
    case DRIVER_LISTERN_CODE:
        recv_listern_data(data.mid(index+1));
        break;
    case DATABASE_LOAD_CODE:
        recv_database_data(data.mid(index+1));
        break;
    default:
        break;
    }
}

void QDeviceManager::recv_sync_data(const QByteArray &data)
{
    XMLObject xml;
    if(!xml.load(data,0))
    {
        return;
    }
    QString device_uuid=xml.get_property("Device_uuid");
    QString opr=xml.get_property("Operator");

    QServerDataSync *sync=m_uuid_to_data.value(device_uuid);
    if(sync==NULL)
    {
        return;
    }

    tagDeviceInfo *info=m_uuid_to_device.value(device_uuid);
    if(info==NULL)
    {
        return;
    }

    if(opr=="Start")
    {
        sync->clear();

        QList<XMLObject*> list=xml.get_children();

        foreach(XMLObject* c,list)
        {
            tagDataInfo *data=new tagDataInfo;
            data->m_uuid=c->get_property("uuid");
            data->m_group=c->get_property("group");
            data->m_name=c->get_property("name");
            data->m_type=c->get_property("type");
            data->m_value=c->get_property("value");
            data->m_lock=c->get_property("locked")=="true";
            data->m_information=c->get_property("information");
            sync->add_data(data);
        }
        emit data_refresh(device_uuid);
    }
    else if(opr=="Changed")
    {
        QList<XMLObject*> l=xml.get_children();
        QStringList list;
        foreach(XMLObject* o,l)
        {
            list<<o->get_property("uuid");
            sync->set_data_value(o->get_property("uuid"),o->get_property("value"));
        }
        emit data_value_refresh(list);
    }
    else if(opr=="Locked_Changed")
    {
        QString uuid=xml.get_property("uuid");
        QString value=xml.get_property("locked");

        tagDataInfo *info=sync->get_data(uuid);
        if(info!=NULL)
        {
            info->m_lock=(value=="true");
            info->m_value=xml.get_property("value");
            QStringList list;
            list<<uuid;
            emit data_value_refresh(list);
        }
    }
}

void QDeviceManager::locked_data(const QString &device_uuid, const QString &uuid, bool locked)
{
    tagDeviceInfo *info=m_uuid_to_device.value(device_uuid);
    if(info==NULL)
    {
        return;
    }
    XMLObject xml;
    xml.set_title("Locked");
    xml.set_property("uuid",uuid);
    xml.set_property("locked",locked?"true":"false");
    xml.set_property("Server_uuid",m_server_info->m_uuid);
    xml.set_property("Device_uuid",device_uuid);
    xml.set_property("Operator","Locked");

    QString str=QString::number(DATA_SYCN_CODE)+":"+xml.write();
    m_device_server->send_data(str.toLocal8Bit(),info->m_device_ip);

}


void QDeviceManager::set_value(const QString &device_uuid, const QString &uuid, const QVariant &value)
{
    tagDeviceInfo *info=m_uuid_to_device.value(device_uuid);
    if(info==NULL)
    {
        return;
    }
    XMLObject xml;
    xml.set_title("SetValue");
    xml.set_property("uuid",uuid);
    xml.set_property("Server_uuid",m_server_info->m_uuid);
    xml.set_property("Device_uuid",device_uuid);
    xml.set_property("Operator","SetValue");
    xml.set_property("value",value.toString());

    QString str=QString::number(DATA_SYCN_CODE)+":"+xml.write();
    m_device_server->send_data(str.toLocal8Bit(),info->m_device_ip);
}

void QDeviceManager::recv_listern_data(const QByteArray &data)
{
    XMLObject xml;
    if(!xml.load(data,0))
    {
        return;
    }
    QString device_uuid=xml.get_property("Device_uuid");
    QString opr=xml.get_property("Operator");

    QServerDriverListern *listern=m_uuid_to_listern.value(device_uuid);
    if(listern==NULL)
    {
        return;
    }

    tagDeviceInfo *info=m_uuid_to_device.value(device_uuid);
    if(info==NULL)
    {
        return;
    }

    if(opr=="Start")
    {
        QList<XMLObject*> list=xml.get_children();

        foreach(XMLObject* o,list)
        {
            QDriverInfo info;
            QList<XMLObject*> l=o->get_children();
            info.m_uuid=o->get_property("Driver_Uuid");
            foreach(XMLObject* oo,l)
            {
                tagDriverProperty p;
                p.m_name=oo->get_property("name");
                p.m_value=oo->get_property("value");
                info.m_propertys.append(p);
            }
            listern->add_driver(info);
        }
        listern->update_driver();
    }
    else if(opr=="Listern")
    {
        QString str=xml.get_property("Message");
        listern->add_data(str);
    }
}

void QDeviceManager::recv_database_data(const QByteArray &data)
{
    if(data.startsWith("?"))
    {
        tagDeviceInfo *info=m_uuid_to_device.value(m_database_uuid);
        if(info==NULL)
        {
            return;
        }
        QByteArray d=data.mid(1);
        int s=d.size()-4;
        int u=0;
        for(int i=0;i<s;i++)
        {
            u+=(d.at(i)&0xff);
        }
        if((d.at(s)&0xff)==((u>>24)&0xff)
                && (d.at(s+1)&0xff)==((u>>16)&0xff)
                && (d.at(s+2)&0xff)==((u>>8)&0xff)
                && (d.at(s+3)&0xff)==(u&0xff))
        {
            m_send_file->add_recv_data(d.left(d.size()-4));
            XMLObject xml;
            xml.set_title("Database");
            xml.set_property("Server_uuid",m_server_info->m_uuid);
            xml.set_property("Device_uuid",m_database_uuid);
            xml.set_property("Operator","Next");

            QString str=QString::number(DATABASE_LOAD_CODE)+":"+xml.write();
            m_device_server->send_data(str.toLocal8Bit(),info->m_device_ip);
            m_retry=0;
            emit database_next();
        }
        else
        {
            m_retry++;
            if(m_retry<3)
            {
                XMLObject xml;
                xml.set_title("Database");
                xml.set_property("Server_uuid",m_server_info->m_uuid);
                xml.set_property("Device_uuid",m_database_uuid);
                xml.set_property("Operator","Re-Send");

                QString str=QString::number(DATABASE_LOAD_CODE)+":"+xml.write();
                m_device_server->send_data(str.toLocal8Bit(),info->m_device_ip);
            }
            else
            {
                m_send_file->reset();
                m_database_uuid="";
                m_retry=0;
                emit database_error("Data Error!");
                return;
            }
        }
    }
    else
    {
        XMLObject xml;
        if(!xml.load(data,0))
        {
            return;
        }
        QString device_uuid=xml.get_property("Device_uuid");
        QString opr=xml.get_property("Operator");

        tagDeviceInfo *info=m_uuid_to_device.value(device_uuid);
        if(info==NULL)
        {
            return;
        }
        if(opr=="End")
        {
            if(m_send_file->save())
            {
                emit database_end();
            }
            else
            {
                emit database_error("Save file failed!");
            }
        }
        else if(opr=="Error")
        {
            emit database_error(xml.get_property("Message"));
        }
        else if(opr=="Start")
        {
            int count=xml.get_property("Message").toInt();
            emit database_count(count);
            XMLObject xml;
            xml.set_title("Database");
            xml.set_property("Server_uuid",m_server_info->m_uuid);
            xml.set_property("Device_uuid",m_database_uuid);
            xml.set_property("Operator","Next");

            QString str=QString::number(DATABASE_LOAD_CODE)+":"+xml.write();
            m_device_server->send_data(str.toLocal8Bit(),info->m_device_ip);
            m_retry=0;
        }
    }
}

void QDeviceManager::start_load_database(const QString &uuid, const QString &name)
{
    tagDeviceInfo *info=m_uuid_to_device.value(uuid);
    if(info==NULL)
    {
        return;
    }
    XMLObject xml;
    xml.set_title("Database");
    xml.set_property("Name",name);
    xml.set_property("Server_uuid",m_server_info->m_uuid);
    xml.set_property("Device_uuid",uuid);
    xml.set_property("Operator","Start");

    QString str=QString::number(DATABASE_LOAD_CODE)+":"+xml.write();
    m_device_server->send_data(str.toLocal8Bit(),info->m_device_ip);

    QDir dir(qApp->applicationDirPath()+"/temp/"+uuid);
    dir.mkpath(qApp->applicationDirPath()+"/temp/"+uuid);
    m_send_file->set_save_path(qApp->applicationDirPath()+"/temp/"+uuid+"/"+name);
    m_database_uuid=uuid;
    m_retry=0;
}
